package org.etlt.job.config.xml;

import org.apache.commons.lang3.StringUtils;
import org.etlt.job.DatasourceResourceSetting;
import org.etlt.job.JobContext;
import org.etlt.job.JobSetting;
import org.etlt.job.ResourceSetting;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.InputStream;
import java.util.List;

public class JobSettingXmlSimplerResolver extends AbstractXmlResolver<JobSetting> {

    public JobSettingXmlSimplerResolver() {

    }

    @Override
    public JobSetting resolve(InputStream stream, JobContext context) {
        try {
            JobSetting jobSetting = new JobSetting();
            parse(jobSetting, stream, context);
            return jobSetting;
        } catch (Exception e) {
            throw new IllegalStateException("job.xml parse error.", e);
        }
    }

    protected void parse(JobSetting jobSetting, InputStream input, JobContext context) throws Exception {
        Document document = getDocumentBuilder().parse(input);
        Element root = document.getDocumentElement();
        NodeParser sp = getParserFactory().get(getXPathExpression(root));
        sp.parse(jobSetting, root, context);
    }


    protected void registerParser() {
        registerParser((jobSetting, node, context) -> {
            Element element = (Element) node;
            jobSetting.setName(element.getAttribute("name"));
            NodeList children = element.getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                Node child = children.item(i);
                if (child.getNodeType() == Node.ELEMENT_NODE) {
                    parseElementForcedly(jobSetting, (Element) child, null);
                }
            }
            return jobSetting;
        }, "/job");

        registerParser((jobSetting, node, context) -> {
            String propertyTag = "property";
            NodeList extractors = ((Element) node).getElementsByTagName(propertyTag);
            for (int i = 0; i < extractors.getLength(); i++) {
                Element property = (Element) extractors.item(i);
                parseElementForcedly(jobSetting, property, context);
            }
            return jobSetting;
        }, "/job/properties");

        registerParser((jobSetting, node, context) -> {
            Element element = (Element) node;
            String name = ((Element) node).getAttribute("name");
            String value = element.getTextContent();
            jobSetting.getProperties().put(name, value);
            return jobSetting;
        }, "/job/properties/property");

        registerParser((jobSetting, node, context) -> {
            Element element = (Element) node;
            NodeList functionObjects = element.getElementsByTagName("functionObject");
            for (int i = 0; i < functionObjects.getLength(); i++) {
                Element functionObject = (Element) functionObjects.item(i);
                String type = functionObject.getAttribute("type");
                if (StringUtils.isNoneEmpty(type))
                    jobSetting.getFunctionTypes().add(type);
            }
            return jobSetting;
        }, "/job/functions");

        registerParser((jobSetting, node, context) -> {
            String par = node.getTextContent();
            if (par != null) jobSetting.setParallel(Integer.parseInt(par));
            return jobSetting;
        }, "/job/parallel");

        registerParser((jobSetting, node, context) -> {
            String extractorTag = "extractor";
            NodeList extractors = ((Element) node).getElementsByTagName(extractorTag);
            for (int i = 0; i < extractors.getLength(); i++) {
                Element extractor = (Element) extractors.item(i);
                parseElementForcedly(jobSetting, extractor, context);
            }
            return jobSetting;
        }, "/job/extractors");

        registerParser(((jobSetting, node, context) -> {
            Element extractorE = (Element) node;
            String name = extractorE.getAttribute("location");
            List<String> extractors = jobSetting.getExtractors();
            extractors.add(name);
            jobSetting.setExtractors(extractors);
            return jobSetting;
        }), "/job/extractors/extractor");

        registerParser(((setting, node, context) -> {
            String loaderTag = "loader";
            NodeList loaders = ((Element) node).getElementsByTagName(loaderTag);
            for (int i = 0; i < loaders.getLength(); i++) {
                Element loader = (Element) loaders.item(i);
                parseElementForcedly(setting, loader, context);
            }
            return setting;
        }), "/job/loaders");

        registerParser(((setting, node, context) -> {
            Element loaderE = (Element) node;
            String location = loaderE.getAttribute("location");
            List<String> loaders = setting.getLoaders();
            loaders.add(location);
            return setting;
        }), "/job/loaders/loader");

        registerParser(((jobSetting, node, context) -> {
            String mappingTag = "mapping";
            NodeList mappings = ((Element) node).getElementsByTagName(mappingTag);
            for (int i = 0; i < mappings.getLength(); i++) {
                Element mapping = (Element) mappings.item(i);
                parseElementForcedly(jobSetting, mapping, context);
            }
            return jobSetting;
        }), "/job/mappings");

        registerParser(((jobSetting, node, context) -> {
            Element mappingE = (Element) node;
            String location = mappingE.getAttribute("location");
            List<String> mappings = jobSetting.getMappings();
            mappings.add(location);
            jobSetting.setMappings(mappings);
            return jobSetting;
        }), "/job/mappings/mapping");

        registerParser(((jobSetting, node, context) -> {
            String resourceTag = "resource";
            Element element = (Element) node;
            NodeList resources = element.getElementsByTagName(resourceTag);
            for (int i = 0; i < resources.getLength(); i++) {
                Element resource = (Element) resources.item(i);
                parseElementForcedly(jobSetting, resource, context);
            }
            return jobSetting;
        }), "/job/resources");

        registerParser(((jobSetting, node, context) -> {
            Element resourceE = (Element) node;
            String name = resourceE.getAttribute("name");
            String type = resourceE.getAttribute("type");
            switch (type) {
                case ResourceSetting.DATA_BASE_RESOURCE:
                    DatasourceResourceSetting resourceSetting = new DatasourceResourceSetting();
                    resourceSetting.setName(name);
                    resourceSetting.setType(type);
                    resourceSetting.setClassName(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "className")));
                    resourceSetting.setUrl(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "url")));
                    resourceSetting.setUser(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "user")));
                    resourceSetting.setPassword(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "password")));
                    resourceSetting.setValidationQuery(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "validationQuery")));
                    resourceSetting.setInitialSize(Integer.parseInt(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "initialSize"))));
                    resourceSetting.setMaxIdle(Integer.parseInt(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "maxIdle"))));
                    resourceSetting.setMaxActive(Integer.parseInt(jobSetting.getPropertyValue(getFirstChildValueByName(resourceE, "maxActive"))));
                    jobSetting.getResources().add(resourceSetting);
                    break;
                default:
                    break;
            }
            return jobSetting;
        }), "/job/resources/resource");

        registerParser((setting, node, context) -> {
            String checklistTag = "checklist";
            NodeList checklists = ((Element) node).getElementsByTagName(checklistTag);
            for (int i = 0; i < checklists.getLength(); i++) {
                Element checklist = (Element) checklists.item(i);
                parseElementForcedly(setting, checklist, context);
            }
            return setting;
        }, "/job/checklists");

        registerParser(((setting, node, context) -> {
            Element element = (Element) node;
            String location = element.getAttribute("location");
            List<String> checklists = setting.getChecklists();
            checklists.add(location);
            return setting;
        }), "/job/checklists/checklist");
    }


}
